package br.com.pentagrama.service;

import java.io.IOException;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.net.telnet.TelnetClient;

import br.com.pentagrama.exception.PingException;
import br.com.pentagrama.exception.ValidateException;
import br.com.pentagrama.service.dto.ResultadoPing;
import br.com.pentagrama.service.dto.ResultadoPorta;

public class TestadorAmbiente implements TestSuite {

	private Logger logger = Logger.getLogger(TestadorAmbiente.class.toString());

	public TelnetClient tc = new TelnetClient();
	
	PingICMP pingICMP = new PingICMP();

	private List<String> rangeIps;

	private List<Integer> portas;

	private List<Integer> portasTCP;

	public TestadorAmbiente() {

	}

	public boolean isHostValido(String host) {
		//FIXME CORRIGIR EXPRESSÃO DE VALIDAÇÃO
		Pattern pattern = Pattern
				.compile("(([0-1]?[0-9]{1,2}\\.)|(2[0-4][0-9]\\.)|(25[0-5]\\.)){3}(([0-1]?[0-9]{1,2})|(2[0-4][0-9])|(25[0-5]))");
		Matcher matcher = pattern.matcher(host);
		if (!matcher.find()) {
			return false;
		}
		return true;
	}

	public boolean isConectavel(String host, int porta) {
		try {
			tc.connect(host, porta);
			logger.info(String
					.format("Conexão efetuada. Host '%s' Porta '%d'.",
							host, porta));
			return true;
		} catch (SocketException e) {
			logger.warning(String
					.format("Host '%s' não aceita conexões na porta '%d'.",
							host, porta));
		} catch (IOException e) {
			logger.warning(String
					.format("Host '%s' não aceita conexões na porta '%d'.",
							host, porta));
		}
		return false;
	}

	public ResultadoPing getLatencia(String host, int vezes) {
		ResultadoPing resultado = new ResultadoPing();
		for (int i = 0; i < vezes; i++) {
			try {
				resultado.resultados.add(testPing(host));
			} catch (PingException pe) {
				resultado.falha();
			}
		}
		return resultado;
	}
	
	public ResultadoPing getLatencia(int vezes) {
		ResultadoPing resultado = new ResultadoPing();
		for(String host: rangeIps) {
			for (int i = 0; i < vezes; i++) {
				try {
					resultado.resultados.add(testPing(host));
				} catch (PingException pe) {
					resultado.falha();
				}
			}
		}
		return resultado;
	}
	
	public double getBanda() {
		double soma = 0.0;
		ResultadoPing rping = getLatencia(5);
		double banda = rping.getBanda();
		System.out.println(String.format(String.format("Banda Calculada: % ,.2f", banda)));
		double bandaMB = banda /1024/1024;
		System.out.println(String.format(String.format("Banda Calculada: % ,.2f", bandaMB)));
		return banda;
	}

	/**
	 * Retorna a diferença em nano segundos do teste de ping para o host do
	 * parâmetro.
	 * 
	 * @param host
	 *            Endereço
	 * @return tempo em nano segundos
	 * @throws PingException
	 *             ocorre caso o host esteja inacessível ou o timeout foi
	 *             excedido
	 */
	public long testPing(String host) throws PingException {
		try {
			long latencia = pingICMP.ping(host);
			String mensagemSucesso = String.format(
					"'%s' alcançado em %d ms", host, latencia);
			logger.info(mensagemSucesso);
			return latencia;
		} catch (Exception e) {
			String mensagemErro = String.format(
					"O host '%s' não foi encontrado.", host);
			logger.warning(mensagemErro);
			throw new PingException(mensagemErro);
		}
	}

	public String getHead(String ip) {
		String ponto = ".";
		String[] partes = ip.split("\\.");
		return partes[0] + ponto + partes[1] + ponto + partes[2];
	}
	
	public int getTail(String ip) {
		String[] partes = ip.split("\\.");
		return Integer.parseInt(partes[3]);
	}
	
	public void addIpRange(String start, String end) throws ValidateException {
		String cabeca = getHead(start);
		int caudaStart = getTail(start);
		int cauldaEnd = getTail(end);
		
		if(caudaStart > cauldaEnd) {
			throw new ValidateException("O parâmetro start deve ser menor ou ingual ao parâmetro end");
		}
		
		if(rangeIps == null) {
			rangeIps = new ArrayList<String>();
		}
		
		for(int i = caudaStart; i <= cauldaEnd; i++) {
			rangeIps.add(cabeca + "." + i);
		}

	}

	public void addPortRange(int start, int end) throws ValidateException {
		if(start > end) {
			throw new ValidateException("O parâmetro start deve ser menor ou ingual ao parâmetro end");
		}
		if(portas == null) {
			portas = new ArrayList<Integer>();
		}
		for(int i = start; i <= end; i++) {
			portas.add(i);
		}
	}
	
	public List<ResultadoPorta> testPorta() {
		List<ResultadoPorta> resultadosPorta = new ArrayList<ResultadoPorta>(); 
		for(String host : rangeIps) {

			for(int i = 0; i < 5; i++) {
				try {
					testPing(host);
				} catch (PingException e) {
					continue;
				}
				
				ResultadoPorta rp = new ResultadoPorta(host);
				for(Integer porta : portas) {
					boolean conecta = isConectavel(host, porta);
					rp.addPorta(porta, conecta);
					resultadosPorta.add(rp);
				}
				
				break;
			}
		}
		return resultadosPorta;
	}

	public void generateReport() {
		// TODO Auto-generated method stub

	}

	public List<String> getRangeIps() {
		return rangeIps;
	}

	public void setRangeIps(List<String> rangeIps) {
		this.rangeIps = rangeIps;
	}

	public List<Integer> getPortas() {
		return portas;
	}

	public void setPortas(List<Integer> portas) {
		this.portas = portas;
	}
	
	
	
}
